#include "Dwarf.Unmanaged.Mpi.h"


Solver :: Solver()
{
    MPI_Comm_rank(MPI_COMM_WORLD, &commRank);           // Retrieve rank of the process
    MPI_Comm_size(MPI_COMM_WORLD, &commSize);           // Retrieve count of processes
    isRootThread = commRank == NUMBER_ROOT_PROCESS;
}

Solver :: ~Solver()
{    

}
//For the demonstration purposes of the project,
//we have chosen depth first search algorithm
void Solver::solve() 
{   
    deque<int> d;
    if(isRootThread)
    {   //Partition the tree for all processors
        partition(d);
        //send the tasks
        for(int i = 1; i < commSize; i++)
        {
            MPI_Send(&(d.back()),1,MPI_INT,i,0,MPI_COMM_WORLD);
            d.pop_back();
        }
    } 
    else
    {   
        //receive the task 
        int recv;
        MPI_Status s;
        MPI_Recv(&recv, 1, MPI_INT, NUMBER_ROOT_PROCESS, 0, MPI_COMM_WORLD, &s);
        d.push_front(recv);
    }

    MPI_Barrier(MPI_COMM_WORLD);

    //Distribute the array of counts.
    MPI_Bcast(graph->visited, graph->getLength(), MPI_INT, NUMBER_ROOT_PROCESS, MPI_COMM_WORLD);

    //while stack not empty 
    while (!d.empty()) 
    {   
        //take the first element
        int u = d.front();
        //for all edges of current vertex
        bool hasNoVisited = true;
        for(int j = 0; j < graph->arrayOfcounts[u]; j++)
        {
            int count =  graph->getVisited(graph->edgesMatrix[u][j]);
            if(count == 0) 
            {   
                //adding new vertex to work stack
                hasNoVisited = false;
                d.push_front(graph->edgesMatrix[u][j]);
                graph->setVisited(graph->edgesMatrix[u][j]);
                break;
            }
        }
        if(hasNoVisited)
        {
            d.pop_front();
        }
    }    
}

//Partition the tree for all processors
void Solver::partition(deque<int>& d) 
{ 
    int headId = graph->getHeadId();
    graph->setVisited(headId);
    d.push_front(headId);
    while((!d.empty()) && ((int)d.size() < commSize))
    {   
        int u = d.front();
        for(int j = 0; j < graph->arrayOfcounts[u]; j++)
        {
            int count =  graph->getVisited(graph->edgesMatrix[u][j]);
            if(count == 0) 
            {   
                //adding new vertex to work queue
                d.push_front(graph->edgesMatrix[u][j]);
                graph->setVisited(graph->edgesMatrix[u][j]);
            }
        }
        d.pop_back();
    }
}

void Solver::gatherResults()
{
    //collect the results on master
    if(!isRootThread)
    {   
        //printf("getLength() %i", solver->graph->getLength());
        MPI_Send(graph->visited, graph->getLength(), MPI_INT, NUMBER_ROOT_PROCESS, 0, MPI_COMM_WORLD);
    } 
    else
    {
        int * recv  = new int[graph->getLength()]; 
        MPI_Status s;
        for(int i = 1; i < commSize; i++)
        {
            MPI_Recv(recv, graph->getLength(), MPI_INT, i, 0, MPI_COMM_WORLD, &s);
            for(int j = 0; j < graph->getLength(); j++)
            {   
                graph->visited[j] += recv[j];
            }
        }
    } 
} 